package no.awi.wpd.probability;


import no.awi.wpd.unit.model.Profile;

public class WarhammerProbabilityFunctions {

	public static final double ONE = 1.0;
	public static final double TWO_THIRDS = 2.0 / 3.0;
	public static final double HALF = 0.5;
	public static final double ONE_THIRD = 1.0 / 3.0;

	public static double getToHitProbability(int attackerWS, int attackeeWS, boolean rerollToHit) {
        double toHitProbability = 0;
		if (attackerWS > attackeeWS) {
			toHitProbability = TWO_THIRDS;
		} else if ((attackeeWS + 0.0) / attackerWS > 2) {
            toHitProbability = ONE_THIRD;
		} else {
            toHitProbability = HALF;
		}
        if (rerollToHit) {
            toHitProbability = ProbabilityFunctions.rerollOnFailure(toHitProbability);
        }
        return toHitProbability;
    }

    public double getAutohitWoundProbability(int strength, int toughness, int armoursave, int wardsave) {
        return 0;
    }

	public static double getToWoundProbability(int strength, int toughness) {
		int delta = strength - toughness;
		delta = delta > 2 ? 2 : delta;
		delta = delta < -2 ? -2 : delta;
		return (delta + 3.0) / 6.0;
	}
	
	public static double getArmourPenetrationProbability(int strength, int armourSave) {
		int modifier = strength > 3 ? strength - 3 : 0;
		int effectiveArmourSave = armourSave + modifier;
		if (effectiveArmourSave > 6) {
			return ONE;
		}
		effectiveArmourSave = effectiveArmourSave < 2 ? 2 : effectiveArmourSave;
		return (effectiveArmourSave - 1) / 6.0;
	}
	
	public static double getWardSavePenetrationProbability(int wardSave) {
		if (wardSave > 6) {
			return ONE;
		}
		return (wardSave - 1) / 6.0;
	}
	
	public static double getProbabilityPassLeadershipTest(ProbabilityDistribution modifier, int leadership, boolean reroll, boolean coldblooded) {
		ProbabilityDistribution rollDistribution;
		if (coldblooded) {
			rollDistribution = getColdBloodedLeadershipTest();
		} else {
			rollDistribution = Dice.TWO_D6();
		}
		if (reroll) {
			rollDistribution = rerollChooseLowest(rollDistribution);
		}
		return getProbabilityPassLeadershipTest(modifier, leadership, rollDistribution);
	}
	
	private static double getProbabilityPassLeadershipTest(ProbabilityDistribution modifier, int leadership, ProbabilityDistribution rollDistribution) {
		double result = 0;
		for (int i = 0; i < modifier.getSize(); i++) {
			for (int j = 0; j < rollDistribution.getSize(); j++) {
				if (modifier.getValueByIndex(i) >= 0  || leadership + modifier.getValueByIndex(i) >= rollDistribution.getValueByIndex(j)) {
					result += modifier.getProbabilityByIndex(i) * rollDistribution.getProbabilityByIndex(j);
				}
			}
		}
		return result;
	}
	
	static ProbabilityDistribution rerollChooseLowest(ProbabilityDistribution distribution) {
		ProbabilityDistribution result = new ProbabilityDistribution(distribution.getSize(), distribution.getMinValue());
		for (int i = 0; i < distribution.getSize(); i++) {
			for (int j = 0; j < distribution.getSize(); j++) {
				result.addProbabilityByIndex(Math.min(i, j), distribution.getProbabilityByIndex(i) * distribution.getProbabilityByIndex(j));
			}
		}
		return result;
	}
	
	static ProbabilityDistribution getColdBloodedLeadershipTest() {
		ProbabilityDistribution distribution = new ProbabilityDistribution(11, 2);
		double distinctOutcomePossibility = 1.0 / Math.pow(6, 3);
		for (int i = 1; i < 7; i++) {
			for (int j = 1; j < 7; j++) {
				for (int k = 1; k < 7; k++) {
					int value = 0;
					if (i >= j && i >= k) { value = j + k; }
					else if (j >= i && j >= k) { value = i + k; }
					else if (k >= i && k >= j) { value = i + j; }
					distribution.addProbabilityByIndex(value - 2, distinctOutcomePossibility);
				}
			}
		}
		return distribution;
	}

    public static ProbabilityDistribution getWoundsPerAttack(Profile attacker, Profile attackee) {
        double toHitProbability = getToHitProbability(attacker.getWs(), attackee.getWs(),
                attacker.rerollToHit(attackee));
        ProbabilityDistribution woundProbability = getAutohitWoundsPerAttack(attacker, attackee);

        return woundProbability.conditionalOrValue(toHitProbability, 0);
     }

    public static ProbabilityDistribution getAutohitWoundsPerAttack(Profile attacker, Profile attackee) {
        //double killingBlowProbability = 1.0 / 6.0;
        double oneWound = getToWoundProbability(attacker.getSt(), attackee.getTo())
                * getArmourPenetrationProbability(attacker.getSt(), attackee.getAsv())
                * getWardSavePenetrationProbability(attackee.getWsv());
        //double killingBlowWound = killingBlowProbability * getWardSavePenetrationProbability(attackee.getWsv());
        return new ProbabilityDistribution(new double[]{ 1 - oneWound, oneWound }, 0);
    }

}
